/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.bootstrap.client.ui;

import com.cleangwt.uibinder.client.TemplateSupport;
import com.cleangwt.user.client.Elements;
import com.cleangwt.user.client.HashTokenizer;
import com.cleangwt.user.client.ui.Button;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.ComplexPanel;

/**
 * Navbars are responsive meta components that serve as navigation headers for your application or
 * site. They begin collapsed (and are toggleable) in mobile views and become horizontal as the
 * available viewport width increases.
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 * @see http://getbootstrap.com/components/#navbar
 */
public class Navbar extends ComplexPanel {
  // base style
  public static final String DEFAULT_STYLE = "navbar-default";
  public static final String INVERSE_STYLE = "navbar-inverse";

  // addon style
  public static final String FIXED_TOP_STYLE = "navbar-fixed-top";

  public static final String FIXED_BOTTOM_STYLE = "navbar-fixed-bottom";

  // element style
  public static final String BRAND_STYLE = "navbar-brand";
  public static final String BUTTON_STYLE = "navbar-btn";
  public static final String COLLAPAE_STYLE = "collapse";
  public static final String FORM_STYLE = "navbar-form";
  public static final String HEADER_STYLE = "navbar-header";
  public static final String LEFT_STYLE = "navbar-left";
  public static final String NAV_STYLE = "navbar-nav";
  public static final String NAVBAR_COLLAPAE_STYLE = "navbar-collapse";
  public static final String NAVBAR_STYLE = "navbar";
  public static final String RIGHT_STYLE = "navbar-right";
  public static final String TEXT_STYLE = "navbar-text";
  public static final String TOGGLE_STYLE = "navbar-toggle";

  /**
   * Creates a navbar that wraps an existing &lt;div&gt; element of template.
   * 
   * @param template the template
   * @param element the element to be wrapped
   */
  public static Navbar wrap(TemplateSupport template, Element element) {
    Navbar widget = new Navbar(element);
    template.wrap(widget);
    return widget;
  }

  /**
   * Resets the style name with base navbar style and the given type style.
   * 
   * @param style the base style
   */
  public void setStyleBase(String style) {
    assert style != null : "Stye must not be null.";
    boolean fixedTop = Elements.hasClass(FIXED_TOP_STYLE, getElement());
    setStyleName(NAVBAR_STYLE + " " + style);
    if (fixedTop) {
      addStyleName(FIXED_TOP_STYLE);
    }
  }

  /**
   * Creates a navbar with the given type style.
   */
  public Navbar() {
    Element navbarToggle = Document.get().createPushButtonElement();
    Element headerElement = Document.get().createDivElement();
    Element collapseElement = Document.get().createDivElement();
    Element brandElement = Document.get().createAnchorElement();
    navbarToggle
        .setInnerHTML("<span class='icon-bar'></span><span class='icon-bar'></span><span class='icon-bar'></span>");
    setElement(Document.get().createElement("nav"));
    getElement().appendChild(headerElement);
    getElement().appendChild(collapseElement);
    Roles.getNavigationRole().set(getElement());
    headerElement.appendChild(navbarToggle);
    headerElement.appendChild(brandElement);
    headerElement.setClassName(HEADER_STYLE);

    collapseElement.setClassName(COLLAPAE_STYLE);
    collapseElement.addClassName(NAVBAR_COLLAPAE_STYLE);
    collapseElement.setId(Document.get().createUniqueId());

    navbarToggle.setAttribute(DataModel.DATA_TOGGLE, DataModel.TOGGLE_COLLAPSE);
    navbarToggle.setAttribute(DataModel.DATA_TARGET, "#" + collapseElement.getId());
    navbarToggle.setClassName(TOGGLE_STYLE);
    brandElement.setAttribute("href", "#" + HashTokenizer.PAGE_TOKEN_PREFIX);
    brandElement.setClassName(BRAND_STYLE);

  }

  /**
   * This constructor may be used by subclasses to explicitly use an existing element. This element
   * must be a &lt;nav&gt; or &lt;div&gt; element.
   * 
   * @param element the element to be used
   */
  protected Navbar(Element e) {
    assert e.getTagName().equalsIgnoreCase("nav") || e.getTagName().equalsIgnoreCase("div");
    setElement(e);
  }

  /**
   * Adds a button.
   * 
   * @param button the button to add
   */
  public void addButton(Button button) {
    button.addStyleName(BUTTON_STYLE);
    add(button, Elements.getByClass(getElement(), Navbar.COLLAPAE_STYLE));
  }

  /**
   * Adds a button with the given pull-right option.
   * 
   * @param button the button to add
   * @param pullRight whether to put the button right
   */
  public void addButton(Button button, boolean pullRight) {
    addButton(button);
    if (pullRight) {
      button.addStyleName(RIGHT_STYLE);
    }
  }

  /**
   * Prepares a inline form panel to add controls.
   */
  public InlineFormPanel addForm() {
    return addForm(false);
  }

  /**
   * Prepares a inline form panel to add controls.
   * 
   * @param pullRight whether to put this form panel right
   */
  public InlineFormPanel addForm(boolean pullRight) {
    InlineFormPanel form = new InlineFormPanel();
    form.addStyleName(FORM_STYLE);
    this.add(form, Elements.getByClass(getElement(), Navbar.COLLAPAE_STYLE));
    if (pullRight) {
      form.addStyleName(RIGHT_STYLE);
    } else {
      form.addStyleName(LEFT_STYLE);
    }
    return form;
  }

  /**
   * Adds a navigator to the navigator container.
   * 
   * @param navigator the navigator to add
   */
  public void addNavigator(Navigator navigator) {
    navigator.setStyleBase(NAV_STYLE);
    this.add(navigator, Elements.getByClass(getElement(), Navbar.COLLAPAE_STYLE));
  }

  /**
   * Adds a navigator with the given pull-right option.
   * 
   * @param navigator the navigator to add
   * @param pullRight whether to pull the navigator right
   */
  public void addNavigator(Navigator navigator, boolean pullRight) {
    addNavigator(navigator);
    if (pullRight) {
      navigator.addStyleName(RIGHT_STYLE);
    } else {
      navigator.addStyleName(LEFT_STYLE);
    }
  }

  /**
   * Adds a text element with the given html.
   * 
   * @param html the html
   */
  public void addText(String html) {
    addText(html, false);
  }

  /**
   * Adds a text element with the given html and pull-right option.
   * 
   * @param html the html
   * @param pullRight whether to pull the text right
   */
  public void addText(String html, boolean pullRight) {
    Element e = Document.get().createDivElement();
    e.setClassName(TEXT_STYLE);
    e.setInnerHTML(html);
    Elements.getByClass(getElement(), Navbar.COLLAPAE_STYLE).appendChild(e);
    if (pullRight) {
      e.addClassName(RIGHT_STYLE);
    }
  }

  /**
   * Sets the brand.
   * 
   * @param html the html
   */
  public void setBrand(String html) {
    Element header = Elements.getByClass(getElement(), HEADER_STYLE);
    Element brand = Elements.getByClass(header, BRAND_STYLE);
    brand.setInnerHTML(html);
  }

}
